// 移植自 Gatsby demo 项目的语法图处理逻辑，适配 VitePress 浏览器环境
import pegjs from 'pegjs'

// 导入本地修改的 railroad-diagrams 文件
import * as rr from './railroad-diagrams/lib/index.mjs'

//May-重新宽度计算函数
function calculateTextWidth(text: string, isBold: boolean = false): number {
  // May-基础字体大小 12px
  const baseWidth = 12
  
  // May-更精确的字符宽度映射
  const charWidths: { [key: string]: number } = {
    // 窄字符
    'i': 0.3, 'l': 0.3, 'I': 0.4, '1': 0.4, 'f': 0.4, 't': 0.4, 'r': 0.4,
    // 中等字符
    'a': 0.5, 'c': 0.5, 'e': 0.5, 'n': 0.5, 'o': 0.5, 's': 0.5, 'u': 0.5, 'v': 0.5, 'x': 0.5, 'z': 0.5,
    'b': 0.6, 'd': 0.6, 'h': 0.6, 'k': 0.6, 'p': 0.6, 'q': 0.6, 'y': 0.6,
    'g': 0.7, 'j': 0.7,
    // 宽字符
    'm': 0.8, 'w': 0.8,
    // 大写字母 - 增加宽度
    'A': 0.7, 'B': 0.7, 'C': 0.7, 'D': 0.7, 'E': 0.6, 'F': 0.6, 'G': 0.7, 'H': 0.7, 'J': 0.5,
    'K': 0.7, 'L': 0.6, 'M': 0.8, 'N': 0.7, 'O': 0.7, 'P': 0.6, 'Q': 0.7, 'R': 0.7, 'S': 0.6, 'T': 0.6,
    'U': 0.7, 'V': 0.7, 'W': 0.9, 'X': 0.7, 'Y': 0.7, 'Z': 0.6,
    // 数字
    '0': 0.6, '2': 0.6, '3': 0.6, '4': 0.6, '5': 0.6, '6': 0.6, '7': 0.6, '8': 0.6, '9': 0.6,
    // 特殊字符
    '_': 0.5, '-': 0.4, '.': 0.2, ',': 0.2, ':': 0.2, ';': 0.2, '!': 0.2, '?': 0.5,
    '(': 0.3, ')': 0.3, '[': 0.3, ']': 0.3, '{': 0.3, '}': 0.3,
    '<': 0.5, '>': 0.5, '=': 0.5, '+': 0.5, '*': 0.4, '/': 0.3, '\\': 0.3,
    '|': 0.2, '&': 0.6, '^': 0.5, '~': 0.5, '`': 0.3, '@': 0.7, '#': 0.6, '$': 0.6, '%': 0.7,
    ' ': 0.3
  }
  
  let totalWidth = 0
  for (const char of text) {
    const charWidth = charWidths[char] || 0.6 // 增加默认宽度
    totalWidth += charWidth * baseWidth
  }
  
  // 如果是粗体，增加宽度
  if (isBold) {
    totalWidth *= 1.2
  }
  
  // 为长文本添加更多的边距
  const padding = Math.min(text.length * 0.8, 35) // 增加边距计算，最多35px
  
  // 特别处理包含大写字母的长单词
  const upperCaseCount = (text.match(/[A-Z]/g) || []).length
  if (upperCaseCount > 0) {
    const upperCaseBonus = upperCaseCount * 3 // 每个大写字母额外3px
    totalWidth += upperCaseBonus
  }
  
  // 特别处理长单词（超过8个字符）
  if (text.length > 8) {
    const longWordBonus = (text.length - 8) * 2 // 每多一个字符额外2px
    totalWidth += longWordBonus
  }
  
  return Math.round(totalWidth + padding)
}

// 设置对齐方式
function setupRailroadOptions() {
  if (rr.Options) {
    rr.Options.INTERNAL_ALIGNMENT = 'left'
  }
}

// 类型定义
type RRComponent = RRLeaf | RRSkip | RRSeq | RRChoice | RROptional | RROneOrMore

interface RRDiagram {
  type: 'Diagram'
  items: RRComponent[]
}

interface RRLeaf {
  type: 'Terminal' | 'NonTerminal' | 'Comment'
  text: string
}

interface RRSkip {
  type: 'Skip'
}

interface RRSeq {
  type: 'Sequence' | 'Stack' | 'OptionalSequence'
  items: RRComponent[]
}

interface RRChoice {
  type: 'Choice'
  normalIndex: 0
  options: RRComponent[]
}

interface RROptional {
  type: 'Optional'
  item: RRComponent
}

interface RROneOrMore {
  type: 'OneOrMore'
  item: RRComponent
  repeat: RRComponent
}

interface CalculatedWidth {
  width: number
}



// EBNF 解析器
const ebnfParser = pegjs.generate(`
  grammar = p:(_ production _)+ {
    return p.map(d => d[1]);
  }

  production = n:nonTerminal _ '::=' _ content:choice {
    return {name: n.text, content};
  }

  choice = f:seq r:(_ '|' _ seq)* {
    let opts = [f]
    r.forEach(n => options.context.appendNodeToChoices(opts, n[3]));
    return opts.length > 1 ? {type: 'Choice', normalIndex: 0, options: opts} : opts[0];
  }

  seq = f:item r:(_ item)* {
    let items = [f];
    r.forEach(n => options.context.appendNodeToSequence(items, n[1]));
    return items.length > 1 ? {type: 'Sequence', items} : items[0];
  }

  item = p:primary _ k:[*+?]? {
    switch (k) {
    case '?':
      return {type: 'Optional', item: p};
    case '+':
      return {type: 'OneOrMore', item: p, repeat: {type: 'Skip'}};
    case '*':
      return options.context.isRTLCapable(p) ?
        {type: 'OneOrMore', item: {type: 'Skip'}, repeat: p} :
        {type: 'Optional', item: {type: 'OneOrMore', item: p, repeat: {type: 'Skip'}}};
    default:
      return p;
    }
  }

  primary = terminal
    / n:nonTerminal !(_ '::=') { return n }
    / '(' _ c:choice _ ')' { return c }

  terminal = ('"' [^"]* '"' / "'" [^']* "'") {
    const t = text();
    return {type: 'Terminal', text: t.substr(1, t.length - 2)};
  }

  nonTerminal = [0-9a-zA-Z_]+ {
    return {type: 'NonTerminal', text: text()};
  }
_ = ([ \\n\\r\\t] / comment)*
comment = '//' (![\\n\\r] .)*
`)

// Railroad 解析器
const railroadParser = pegjs.generate(`
  grammar = 'Diagram('_ its:items _')' {
    return [{type: 'Sequence', items: its}];
  }

  items = its:(_ item _',')* {
    return its.map(i => i[1]);
  }

  item = n:nonTerminal { return n }
    / c:comment { return c }
    / s:seq { return s }
    / c:choice { return c }
    / o:oneormore { return o }
    / o:opt { return o }

  seq = s:stack { return s }
    / s:sequence { return s }
    / ops:opSequence { return ops }

  nonTerminal = 'NonTerminal(' _ '"' t:[^"]* '"' _ ')' {
    return {type: 'NonTerminal', text: t.join('')};
  }

  comment = 'Comment(' _ '"' t:[^"]* '"' _ ')' {
    return {type: 'Comment', text: t.join('')};
  }

  stack = 'Stack(' _ its: items _ ')' {
    return {type: 'Stack', items: its};
  }

  sequence = 'Sequence(' _ its: items _ ')' {
    return {type: 'Sequence', items: its};
  }

  opSequence = 'OptionalSequence(' _ its: items _ ')' {
    return {type: 'OptionalSequence', items: its};
  }

  choice = 'Choice(' _ n: [0-9]+ _ ','_ its: items _ ')' {
    return {type: 'Choice', normalIndex: n, options: its};
  }

  oneormore = 'OneOrMore(' _ i: item _ ',' _ r: item _ ',' _ ')' {
    return {type: 'OneOrMore', item: i, repeat: r};
  }

  opt = 'Optional(' _ '"' t:[^"]* '"' _ ')' {
    return {type: 'Optional', item: {type: 'NonTerminal', text: t.join('')}};
  }

  _ = [ \\n\\r\\t]*
`)

/**
 * Checks if two EBNF nodes are equivalent.
 */
function deepEq<T extends RRComponent>(a: T, b: T): boolean {
  if (a.type !== b.type) {
    return false
  }

  switch (a.type) {
    case 'Skip':
      return true
    case 'Terminal':
    case 'NonTerminal':
      return a.text === (b as RRLeaf).text
    case 'Optional':
      return deepEq(a.item, (b as RROptional).item)
    case 'OneOrMore':
      return (
        deepEq(a.item, (b as RROneOrMore).item) &&
        deepEq(a.repeat, (b as RROneOrMore).repeat)
      )
    case 'Choice': {
      const aa = a.options
      const bb = (b as RRChoice).options
      return aa.length === bb.length && aa.every((n, i) => deepEq(n, bb[i]))
    }
    case 'Sequence':
    case 'OptionalSequence':
    case 'Stack': {
      const aa = a.items
      const bb = (b as RRSeq).items
      return aa.length === bb.length && aa.every((n, i) => deepEq(n, bb[i]))
    }
    default:
      return false
  }
}

/**
 * Checks whether the component can be read from right to left.
 */
function isRTLCapable(a: RRComponent): boolean {
  switch (a.type) {
    case 'Skip':
    case 'Terminal':
    case 'NonTerminal':
      return true
    case 'Optional':
      return isRTLCapable(a.item)
    case 'OneOrMore':
      return isRTLCapable(a.item) && isRTLCapable(a.repeat)
    case 'Choice':
      return a.options.every(isRTLCapable)
    case 'Sequence':
    case 'OptionalSequence':
    case 'Stack': {
      const length = a.items.length
      return length === 1 ? isRTLCapable(a.items[0]) : length === 0
    }
    default:
      return false
  }
}

/**
 * 检测前缀
 */
function findCommonPrefixForMultiple(sequences: RRSeq[]): RRComponent[] {
  if (sequences.length === 0) return []
  if (sequences.length === 1) return sequences[0].items
  
  let commonPrefix = sequences[0].items
  
  for (let i = 1; i < sequences.length; i++) {
    const currentPrefix = findCommonPrefix(commonPrefix, sequences[i].items)
    if (currentPrefix.length < commonPrefix.length) {
      commonPrefix = currentPrefix
    }
  }
  
  return commonPrefix
}

/**
 * 检测共同前缀
 */
function findCommonPrefix(seq1: RRComponent[], seq2: RRComponent[]): RRComponent[] {
  const minLength = Math.min(seq1.length, seq2.length)
  const commonPrefix: RRComponent[] = []
  
  for (let i = 0; i < minLength; i++) {
    if (deepEq(seq1[i], seq2[i])) {
      commonPrefix.push(seq1[i])
    } else {
      break
    }
  }
  
  return commonPrefix
}

/**
 * 从序列中移除前缀
 */
function removePrefix(seq: RRComponent[], prefixLength: number): RRComponent[] {
  return seq.slice(prefixLength)
}

/**
 * 优化结构
 */
function createOptimizedChoice(commonPrefix: RRComponent[], options: RRComponent[]): RRComponent {
  if (options.length === 1) {
    const firstOption = options[0] as RRSeq
    return { type: 'Sequence', items: [...commonPrefix, ...firstOption.items] }
  }
  
  return {
    type: 'Sequence',
    items: [
      ...commonPrefix,
      { type: 'Choice', normalIndex: 0, options }
    ]
  }
}


function appendNodeToChoices(opts: RRComponent[], node: RRComponent): void {
  // 原有的优化逻辑
  if (node.type === 'Sequence' && node.items.length >= 2) {
    const a = node.items[0]
    const b = node.items[node.items.length - 1]
    const last = opts[opts.length - 1]
    let optItems: RRComponent[] | undefined = undefined
    if (a.type === 'Optional' && deepEq(a.item, last)) {
      node.items.shift()
      optItems = [last, node]
    }
    if (!optItems && b.type === 'Optional' && deepEq(b.item, last)) {
      node.items.pop()
      optItems = [node, last]
    }
    if (optItems) {
      opts[opts.length - 1] = { type: 'OptionalSequence', items: optItems }
      return
    }
  }
  
  // 添加检测重复前缀
  if (node.type === 'Sequence') {
    // 获取序列选项
    const sequenceOptions: RRSeq[] = []
    
    // check-options
    sequenceOptions.push(node)
    
    for (const opt of opts) {
      if (opt.type === 'Sequence') {
        sequenceOptions.push(opt as RRSeq)
      }
    }
    
    // >=2，找到共同前缀
    if (sequenceOptions.length >= 2) {
      const commonPrefix = findCommonPrefixForMultiple(sequenceOptions)
      
      // 找到就进行优化
      if (commonPrefix.length >= 2) {
        const prefixLength = commonPrefix.length
        
        const remainingOptions: RRSeq[] = sequenceOptions.map(seq => ({
          type: 'Sequence' as const,
          items: removePrefix(seq.items, prefixLength)
        }))
        
        const optimizedChoice = createOptimizedChoice(commonPrefix, remainingOptions)
        
        opts.length = 0
        opts.push(optimizedChoice)
        return
      }
    }
  }
  
  opts.push(node)
}

/**
 * Appends an EBNF node to a Sequence container.
 * Optimizes patterns like `a (b a)*` into OneOrMore.
 */
function appendNodeToSequence(items: RRComponent[], node: RRComponent): void {
  if (
    node.type === 'Optional' &&
    node.item.type === 'OneOrMore' &&
    node.item.repeat.type === 'Skip' &&
    node.item.item.type === 'Sequence' &&
    node.item.item.items.length === 2
  ) {
    const left = items[items.length - 1]
    const [repeat, right] = node.item.item.items
    if (isRTLCapable(repeat) && deepEq(left, right)) {
      items[items.length - 1] = {
        type: 'OneOrMore',
        item: left,
        repeat,
      }
      return
    }
  }
  items.push(node)
}

// 自定义 Start/End 样式，模仿 bottlecaps.de
function setupCustomStyles() {
  if (rr.Start) {
    rr.Start.prototype.format = function (x: number, y: number) {
      let path = new rr.Path(x, y - 4)
      path.attrs.d += 'v8l8-4Zm8 0v8l8-4Zm8 4h4' // ▶▶-
      path.attrs.class = 'start-end'
      path.addTo(this)
      return this
    }
  }
  
  if (rr.End) {
    rr.End.prototype.format = function (x: number, y: number) {
      this.attrs.d = `M${x} ${y}h4m0-4v8l16-8v8Z` // -▶◀
      this.attrs.class = 'start-end'
      return this
    }
  }
}

/**
 * Converts EBNF nodes to railroad components with proportional font width.
 */
function toRailroad(node: RRComponent): any {
  switch (node.type) {
    case 'Skip':
      return new rr.Skip()
    case 'Terminal': {
      let t = new rr.Terminal(node.text)
      t.width = calculateTextWidth(node.text, true) + 30
      return t
    }
    case 'NonTerminal': {
      let nt = new rr.NonTerminal(node.text)
      nt.width = calculateTextWidth(node.text, false) + 30
      return nt
    }
    case 'Comment':
      return new rr.Comment(node.text)
    case 'Optional':
      return new rr.Optional(toRailroad(node.item))
    case 'OneOrMore':
      return new rr.OneOrMore(toRailroad(node.item), toRailroad(node.repeat))
    case 'Choice':
      return new rr.Choice(0, ...node.options.map(toRailroad))
    case 'OptionalSequence':
      return new rr.OptionalSequence(...node.items.map(toRailroad))
    case 'Sequence':
      return new rr.Sequence(...node.items.map(toRailroad))
    case 'Stack':
      return new rr.Stack(...node.items.map(toRailroad))
  }
}

/**
 * 解析 EBNF 语法并生成语法图
 */
export function parseEbnfAndGenerateDiagrams(content: string): Array<{name: string, svg: string}> {
  try {
    // 初始化 railroad 库
    setupRailroadOptions()
    setupCustomStyles()
    
    const grammar = ebnfParser.parse(content, {
      context: {
        appendNodeToChoices,
        appendNodeToSequence,
        isRTLCapable,
      },
    })
    
    return grammar.map(({ name, content }: any) => {
      let svg = ''
      try {
        const diagram = new rr.Diagram(toRailroad(content)).format(2)
        svg = diagram.toString()
        svg = svg.replace(/<path([^>]*?)stroke-width="[^"]*"/g, '<path$1stroke-width="1"')
      } catch (e) {
        svg = `<div style='color:red'>${name} 语法图生成失败: ${e.message}</div>`
        svg = svg.replace(
          /(<text[^>]*?>)(;)(<\/text>)/g,
          '$1<tspan font-size="22"  font-weight="bold">;</tspan>$3'
        )
      }
      return { name, svg }
    })
  } catch (e) {
    console.error('EBNF 解析失败:', e)
    return []
  }
}

/**
 * 解析 Railroad 语法并生成语法图
 */
export function parseRailroadAndGenerateDiagrams(content: string): Array<{name: string, svg: string}> {
  try {
    // 初始化 railroad 库
    setupRailroadOptions()
    setupCustomStyles()
    
    const grammar = railroadParser.parse(content, {
      context: {
        appendNodeToChoices,
        appendNodeToSequence,
        isRTLCapable,
      },
    })
    
    return grammar.map((node: any, index: number) => {
      let svg = ''
      try {
        const diagram = new rr.Diagram(toRailroad(node)).format(2)
        svg = diagram.toString()
        svg = svg.replace(/<path([^>]*?)stroke-width="[^"]*"/g, '<path$1stroke-width="1"')
      } catch (e) {
        svg = `<div style='color:red'>语法图生成失败: ${e.message}</div>`
      }
      return { name: `Diagram${index + 1}`, svg }
    })
  } catch (e) {
    console.error('Railroad 解析失败:', e)
    return []
  }
}

/**
 * 自动检测语法类型并生成语法图
 */
export function generateSyntaxDiagrams(content: string): Array<{name: string, svg: string}> {
  // 检测语法类型
  if (/^Diagram\(/.test(content.trim())) {
    return parseRailroadAndGenerateDiagrams(content)
  } else if (/::=/.test(content)) {
    return parseEbnfAndGenerateDiagrams(content)
  } else {
    // 默认按 EBNF 处理
    return parseEbnfAndGenerateDiagrams(content)
  }
}

function addArrowMarkers(svg: string): string {
  if (!svg.includes('<svg')) return svg

  // 添加红色箭头定义
  const arrowDefs = `
    <defs>
      <marker id="arrowhead-red" markerWidth="10" markerHeight="7" 
        refX="9" refY="3.5" orient="auto">
        <polygon points="0 0, 10 3.5, 0 7" fill="red" />
      </marker>
    </defs>
  `
  svg = svg.replace('<svg', '<svg' + arrowDefs)



  svg = svg.replace(
    /<path([^>]*?)(stroke=(['"])(#333|black)\3|stroke-width=(['"])2\5)([^>]*)>/g,
    (match, attrs1, _s, _q, _color, _swq, _sw, attrs2) => {
      if (match.includes('marker-end')) return match
      return `<path${attrs1}stroke="red" stroke-width="0.8"${attrs2} marker-end="url(#arrowhead-red)" />`
    }
  )

  return svg
}